/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package merlion.common.session.stateless;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import merlion.common.entity.AccessRight;
import merlion.common.entity.SecurityRole;
import merlion.common.entity.Staff;
import merlion.common.entity.SystemUserAccount;
import merlion.common.util.Consts;
import merlion.common.util.CryptographicHelper;
import merlion.common.util.exception.NotExistException;
import merlion.common.util.exception.EntityExistException;
import merlion.common.util.exception.UpdateFailureException;

/**
 *
 * @author Zhang Ying
 */
@Stateless
public class SystemUserSessionBean implements SystemUserSessionBeanLocal, SystemUserSessionBeanRemote {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public Collection<SecurityRole> getAllSecurityRoles() {
        Query query = entityManager.createQuery("SELECT sr FROM SecurityRole sr");
        return (List<SecurityRole>) query.getResultList();
    }

    @Override
    public Long addNewSecurityRole(String roleName, String department, Collection<AccessRight> selectedRights) throws EntityExistException {
        Query query = entityManager.createQuery("SELECT sr FROM SecurityRole sr WHERE sr.roleName='" + roleName + "'");
        if (query.getResultList().isEmpty()) {
            SecurityRole newSR = new SecurityRole();
            newSR.create(roleName, department);
            newSR.setAccessRights(selectedRights);
            entityManager.merge(newSR);
            return newSR.getId();
        } else {
            throw new EntityExistException("The role name is already in exist.");
        }
    }

    @Override
    public Long deleteSecurityRole(Long id) throws NotExistException {
        SecurityRole sr = entityManager.find(SecurityRole.class, id);
        if (sr == null) {
            throw new NotExistException("The role is not in exist");
        } else {
            // refresh all the staff with this role
            Query query = entityManager.createQuery("SELECT s FROM Staff s");
            Collection<Staff> staff = query.getResultList();
            for (Staff s : staff) {
                s.getSecurityRoles().remove(sr);
            }
            Collection<AccessRight> ars = sr.getAccessRights();
            sr.getAccessRights().removeAll(ars);
            entityManager.flush();
            entityManager.remove(sr);
        }
        return id;
    }

    @Override
    public Collection<AccessRight> getAllAccessRights() {
        Query query = entityManager.createQuery("SELECT ar FROM AccessRight ar");
        return (List<AccessRight>) query.getResultList();
    }

    @Override
    public void addNewStaff(Staff newStaff) throws EntityExistException {
        Query query = entityManager.createQuery("SELECT s FROM Staff s WHERE s.systemUserAccount.username='"
                + newStaff.getSystemUserAccount().getUsername() + "'");
        if (query.getResultList().isEmpty()) {
            entityManager.merge(newStaff);
        } else {
            throw new EntityExistException("Staff already exists");
        }
    }

    @Override
    public boolean checkUsernameExist(String username) {
        Query query = entityManager.createQuery("SELECT sua FROM SystemUserAccount sua WHERE sua.username='" + username + "'");
        return !query.getResultList().isEmpty();
    }

    @Override
    public SystemUserAccount updateSystemUserAccountStatus(String token) throws UpdateFailureException {
        Query query = entityManager.createQuery("SELECT sua FROM SystemUserAccount sua WHERE sua.token='"
                + token + "' AND sua.status='" + Consts.ACCOUNT_DISABLED + "'");
        if (query.getResultList().isEmpty()) {
            throw new UpdateFailureException("Token not exists");
        } else {
            SystemUserAccount sua = (SystemUserAccount) query.getResultList().get(0);
            sua.setToken(null);
            entityManager.flush();
            return sua;
        }
    }

    @Override
    public boolean checkSystemUserAccountMatchDisabled(String username, String unencryptedPassword) {
        CryptographicHelper cryptographicHelper = new CryptographicHelper();
        String encryptedPassword = cryptographicHelper.doAESEncryptString(unencryptedPassword,
                cryptographicHelper.getDefaultEncryptionKey(), cryptographicHelper.getDefaultEncryptionIV());
        Query query = entityManager.createQuery("SELECT sua FROM SystemUserAccount sua WHERE sua.username='"
                + username + "' AND sua.encryptedPassword=:p1 AND sua.status='" + Consts.ACCOUNT_DISABLED + "'");
        query.setParameter("p1", encryptedPassword);
        return !query.getResultList().isEmpty();
    }

    @Override
    public boolean checkSystemUserAccountMatch(String username, String unencryptedPassword) {
        CryptographicHelper cryptographicHelper = new CryptographicHelper();
        String encryptedPassword = cryptographicHelper.doAESEncryptString(unencryptedPassword,
                cryptographicHelper.getDefaultEncryptionKey(), cryptographicHelper.getDefaultEncryptionIV());
        Query query = entityManager.createQuery("SELECT sua FROM SystemUserAccount sua WHERE sua.username='"
                + username + "' AND sua.encryptedPassword=:p1 AND sua.status='" + Consts.ACCOUNT_ACTIVATED + "'");
        query.setParameter("p1", encryptedPassword);
        return !query.getResultList().isEmpty();
    }

    // private methods
    @Override
    public Collection<AccessRight> getStaffAccessRights(String staffUsername) {
        Collection<AccessRight> accessRights = new ArrayList<AccessRight>();
        Query query1 = entityManager.createQuery("SELECT s.securityRoles FROM Staff s WHERE s.systemUserAccount.username=:p1");
        query1.setParameter("p1", staffUsername);
        List<SecurityRole> roles = query1.getResultList();
        for (SecurityRole sr : roles) {
            Collection<AccessRight> ars = sr.getAccessRights();
            ars.removeAll(accessRights);
            accessRights.addAll(ars);
        }
        return accessRights;
    }

    @Override
    public AccessRight getAccessRight(Long id) {
        return entityManager.find(AccessRight.class, id);
    }

    @Override
    public Long updateSecurityRole(SecurityRole newRole) throws UpdateFailureException {
        try {
            entityManager.merge(newRole);
            return newRole.getId();
        } catch (Exception ex) {
            throw new UpdateFailureException("Updating Security Role's attributes fails.");
        }
    }

    @Override
    public Long updateSecurityRole(SecurityRole newRole, Collection<AccessRight> updateAccessRights) throws UpdateFailureException {
        try {
            newRole.setAccessRights(updateAccessRights);
            entityManager.merge(newRole);
            return newRole.getId();
        } catch (Exception ex) {
            throw new UpdateFailureException("Updating Security Role's attributes fails.");
        }
    }

    @Override
    public SecurityRole getSecurityRole(Long id) {
        return entityManager.find(SecurityRole.class, id);
    }

    @Override
    public Collection<SystemUserAccount> getAllSystemUserAccounts() {
        Query query = entityManager.createQuery("SELECT sua FROM SystemUserAccount sua");
        return (List<SystemUserAccount>) query.getResultList();
    }

    @Override
    public SystemUserAccount getSystemUserAccount(Long id) {
        return entityManager.find(SystemUserAccount.class, id);
    }

    @Override
    public void updateSystemUserAccount(SystemUserAccount sua, Collection<SecurityRole> updatedSecuirtyRoles) throws UpdateFailureException {
        try {
            sua.getStaff().setSecurityRoles(updatedSecuirtyRoles);
            entityManager.merge(sua);
        } catch (Exception ex) {
            throw new UpdateFailureException("Updating system user account's attributes fails.");
        }
    }

    @Override
    public void updateSystemUserAccount(SystemUserAccount sua) throws UpdateFailureException {
        try {
            entityManager.merge(sua);
        } catch (Exception ex) {
            throw new UpdateFailureException("Updating system user account's attributes fails.");
        }
    }

    @Override
    public void deleteSystemUserAccount(Long id) throws NotExistException {
        SystemUserAccount sua = entityManager.find(SystemUserAccount.class, id);
        if (sua == null) {
            throw new NotExistException("The role is not in exist");
        } else {
            Collection<SecurityRole> srs = sua.getStaff().getSecurityRoles();
            for (SecurityRole sr : srs) {
                Collection<AccessRight> ars = sr.getAccessRights();
                sr.getAccessRights().removeAll(ars);
            }
            entityManager.flush();
            sua.getStaff().getSecurityRoles().removeAll(srs);
            entityManager.flush();
            entityManager.remove(sua);
        }

        SecurityRole sr = entityManager.find(SecurityRole.class, id);
        if (sr == null) {
        } else {
            // refresh all the staff with this role
            Query query = entityManager.createQuery("SELECT s FROM Staff s");
            Collection<Staff> staff = query.getResultList();
            for (Staff s : staff) {
                s.getSecurityRoles().remove(sr);
            }
            Collection<AccessRight> ars = sr.getAccessRights();
            sr.getAccessRights().removeAll(ars);
            entityManager.flush();
            entityManager.remove(sr);
        }
    }

    @Override
    public Staff getStaff(String accountUsername) throws NotExistException {
        Query query = entityManager.createQuery("SELECT s FROM Staff s WHERE s.systemUserAccount.username = '" + accountUsername + "'");
        if (query.getResultList().isEmpty()) {
            throw new NotExistException("Staff whose username is " + accountUsername + " does not exist");
        } else {
            return (Staff) query.getResultList().get(0);
        }
    }

    @Override
    public void updateStaffToken(String staffUsername, String token) throws UpdateFailureException {
        Query query = entityManager.createQuery("SELECT s FROM SystemUserAccount s WHERE s.username='" + staffUsername + "'");
        if (query.getResultList().isEmpty()) {
            throw new UpdateFailureException("Sorry the staff doesn't exist and update fails.");
        } else {
            SystemUserAccount sua = (SystemUserAccount) query.getResultList().get(0);
            sua.setToken(token);
            entityManager.flush();
        }
    }

    @Override
    public boolean isTokenValid(String token) {
        Query query = entityManager.createQuery("SELECT sua.token FROM SystemUserAccount sua");
        if (query.getResultList().contains(token)) {
            return Boolean.TRUE;
        } else {
            return Boolean.FALSE;
        }
    }

    @Override
    public Staff activateSytemUserAccount(SystemUserAccount sua) throws UpdateFailureException {
        try {
            sua.setStatus(Consts.ACCOUNT_ACTIVATED);
            entityManager.merge(sua);
            return sua.getStaff();
        } catch (Exception ex) {
            throw new UpdateFailureException("Updating system user account's attributes fails.");
        }
    }

    @Override
    public SystemUserAccount deactivateSystemUserAccount(String staffUsername) {
        Query query = entityManager.createQuery("SELECT sua FROM SystemUserAccount sua WHERE sua.username=:p1");
        query.setParameter("p1", staffUsername);
        SystemUserAccount sua = (SystemUserAccount) query.getResultList().get(0);
        sua.setStatus(Consts.ACCOUNT_DISABLED);
        entityManager.flush();
        return sua;
    }

    @Override
    public boolean checkEmailExist(String emailInputed) {
        Query query = entityManager.createQuery("SELECT sua FROM SystemUserAccount sua WHERE sua.emailAddress=:p1");
        query.setParameter("p1", emailInputed);
        if (query.getResultList().isEmpty()) {
            return Boolean.FALSE;
        } else {
            return Boolean.TRUE;
        }
    }

    @Override
    public String getSecureQuestion(String email) {
        Query query = entityManager.createQuery("SELECT sua FROM SystemUserAccount sua WHERE sua.emailAddress=:p1");
        query.setParameter("p1", email);
        SystemUserAccount sua = (SystemUserAccount) query.getResultList().get(0);
        if (sua == null) {
            return "No question??";
        }
        return sua.getQuestion1();
    }

    @Override
    public boolean checkSecureAnswer(String email, String ans) {
        Query query = entityManager.createQuery("SELECT sua FROM SystemUserAccount sua WHERE sua.emailAddress=:p1 AND sua.answer1=:p2");
        query.setParameter("p1", email);
        query.setParameter("p2", ans);
        if (query.getResultList().isEmpty()) {
            return Boolean.FALSE;
        } else {
            return Boolean.TRUE;
        }
    }

    @Override
    public SystemUserAccount getSystemUserAccount(String email) {
        Query query = entityManager.createQuery("SELECT sua FROM SystemUserAccount sua WHERE sua.emailAddress=:p1");
        query.setParameter("p1", email);
        return (SystemUserAccount) query.getResultList().get(0);
    }

    @Override
    public void updateLoginTime(String staffUsername) {
        Query query = entityManager.createQuery("SELECT sua FROM SystemUserAccount sua WHERE sua.username=:p1");
        query.setParameter("p1", staffUsername);
        if (!query.getResultList().isEmpty()) {
            SystemUserAccount sua = (SystemUserAccount) query.getResultList().get(0);
            Calendar calendar = Calendar.getInstance();
            sua.setLastLoginTime(new Timestamp(calendar.getTime().getTime()));
            entityManager.flush();
        }
    }

    @Override
    public void updateCurrentStaffInfo(Staff currentStaff) throws UpdateFailureException {
        try {
            entityManager.merge(currentStaff);
        } catch (Exception ex) {
            throw new UpdateFailureException("Updating staff info fails.");
        }
    }

    @Override
    public Collection<Staff> getAllStaff() {
        Query query = entityManager.createQuery("SELECT s FROM Staff s");
        return (List<Staff>) query.getResultList();
    }

    @Override
    public Collection<Staff> getAllStaffbyDepartment(String department) {
        Query query = entityManager.createQuery("SELECT s FROM Staff s WHERE s.department LIKE :p1");
        query.setParameter("p1", department);
        return (List<Staff>) query.getResultList();
    }

    @Override
    public Collection<String> getAllDepartments() {
        Query query = entityManager.createQuery("SELECT DISTINCT sr.department FROM SecurityRole sr");
        return (List<String>) query.getResultList();
    }

    @Override
    public boolean checkRolenameExist(String nameInputed) {
        Query query = entityManager.createQuery("SELECT sr FROM SecurityRole sr WHERE sr.roleName='" + nameInputed + "'");
        return !query.getResultList().isEmpty();
    }

    @Override
    public boolean checkAccountStatusActivated(String username) {
        Query query = entityManager.createQuery("SELECT sua FROM SystemUserAccount sua WHERE sua.username='"
                + username + "' AND sua.status='" + Consts.ACCOUNT_ACTIVATED + "'");
        return !query.getResultList().isEmpty();
    }

    @Override
    public boolean checkAccountDisabled(String emailInputed) {
        Query query = entityManager.createQuery("SELECT sua FROM SystemUserAccount sua WHERE sua.emailAddress='"
                + emailInputed + "' AND sua.status='" + Consts.ACCOUNT_DISABLED + "'");
        return !query.getResultList().isEmpty();
    }

    @Override
    public boolean isAbleToDeleteRole(SecurityRole selectedRole) {
        Query query = entityManager.createQuery("SELECT s FROM Staff s");
        Collection<Staff> staff = (Collection<Staff>) query.getResultList();
        for (Staff s : staff) {
            if (s.getSecurityRoles().contains(selectedRole)
                    && s.getSecurityRoles().size() == 1) {
                return false;
            }
        }
        return true;
    }

    @Override
    public Collection<SystemUserAccount> getFilteredAccounts(String rolename) {
        Query query = entityManager.createQuery("SELECT sua FROM SystemUserAccount sua");
        Query query2 = entityManager.createQuery("SELECT sr FROM SecurityRole sr WHERE sr.roleName=:p1");
        query2.setParameter("p1", rolename);
        SecurityRole sr = (SecurityRole) query2.getSingleResult();
        List<SystemUserAccount> accs = (List<SystemUserAccount>) query.getResultList();
        Collection<SystemUserAccount> result = new ArrayList<SystemUserAccount>();
        for (SystemUserAccount sua : accs) {
            if (sua.getStaff().getSecurityRoles().contains(sr)) {
                result.add(sua);
            }
        }
        return result;
    }

    @Override
    public Staff getStaff(Long id) {
        return entityManager.find(Staff.class, id);
    }

    public SecurityRole getSecurityRole(String roleName) {
        Query query = entityManager.createQuery("SELECT sr FROM SecurityRole sr WHERE sr.roleName=:p1");
        query.setParameter("p1", roleName);
        SecurityRole sr = (SecurityRole) query.getSingleResult();
        return sr;
    }

    @Override
    public boolean checkStaffIsSalesMgmtLevel(Long id) {
        try {
            Staff cf = entityManager.find(Staff.class, id);
            SecurityRole sr1 = getSecurityRole(Consts.SALES_VP_ROLE);
            SecurityRole sr2 = getSecurityRole(Consts.SALES_MANAGER_ROLE);
            if (cf.getSecurityRoles().contains(sr1) || cf.getSecurityRoles().contains(sr2)) {
                return true;
            } else {
                return false;
            }
        } catch (Exception ex){
            ex.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean checkStaffIsFinMgmtLevel(Long id) {
        try {
            Staff cf = entityManager.find(Staff.class, id);
            SecurityRole sr1 = getSecurityRole(Consts.FIN_VP_ROLE);
            if (cf.getSecurityRoles().contains(sr1)) {
                return true;
            } else {
                return false;
            }
        } catch (Exception ex){
            ex.printStackTrace();
            return false;
        }
    }

    @Override
    public SystemUserAccount getRandomProductionMgmtLevelAccount() {
        SecurityRole sr1 = getSecurityRole(Consts.MRP_VP_ROLE);
        SecurityRole sr2 = getSecurityRole(Consts.MRP_MANAGER_ROLE);
        Query query = entityManager.createQuery("SELECT s FROM Staff s");
        Collection<Staff> ss = query.getResultList();
        if (sr1 == null && sr2 == null) {
            return null;
        } else if (sr1 == null && sr2 != null) {
            for (Staff s : ss) {
                if (s.getSecurityRoles().contains(sr2) ) {
                    return s.getSystemUserAccount();
                }
            }
        } else if (sr1 != null && sr2 == null) {
            for (Staff s : ss) {
                if (s.getSecurityRoles().contains(sr1) ) {
                    return s.getSystemUserAccount();
                }
            }
        } else {
            for (Staff s : ss) {
                if (s.getSecurityRoles().contains(sr1) || s.getSecurityRoles().contains(sr2) ) {
                    return s.getSystemUserAccount();
                }
            }
        }
        return null;
    }
}
